use crate::R;
use axum::extract::rejection::{FormRejection, JsonRejection, PathRejection, QueryRejection};
use axum::response::{IntoResponse, Response};
use log::error;
use std::fmt::{Display, Formatter};
use axum_extra::typed_header::TypedHeaderRejection;

/// 自定义错误
#[derive(Debug, thiserror::Error)]
pub enum ApiError {
    /// 业务错误
    #[error("{0}")]
    Biz(String),

    /// 认证错误
    #[error("{0}")]
    Authorization(Auth),

    /// 路径参数错误
    #[error("路径参数错误: {0}")]
    Path(#[from] PathRejection),

    /// 查询参数错误
    #[error("查询参数错误: {0}")]
    Query(#[from] QueryRejection),

    /// 请求参数错误
    #[error("请求参数错误: {0}")]
    Json(#[from] JsonRejection),

    /// 表单参数错误
    #[error("表单参数错误: {0}")]
    Form(#[from] FormRejection),

    /// 参数校验错误
    #[error("参数错误: {0}")]
    Validation(#[from] validator::ValidationErrors),
    
    #[cfg(feature = "redis")]
    /// redis错误
    #[error("{0}")]
    Redis(#[from] redis::RedisError)
}


impl From<TypedHeaderRejection> for ApiError {
    fn from(value: TypedHeaderRejection) -> Self {
        error!("请求头错误：{value}");
        ApiError::Biz(format!("{}请求头错误", value.name()))
    }
}

/// 为自定义的[ApiError]实现 [IntoResponse] trait
impl IntoResponse for ApiError {
    fn into_response(self) -> Response {
        R::<()>::from(self).into_response()
    }
}

impl From<ApiError> for Response {
    fn from(value: ApiError) -> Self {
        value.into_response()
    }
}

#[derive(Debug)]
/// 认证错误
pub enum Auth {
    /// 未登录
    NoToken,
    /// token过期
    TokenExpire,
}

impl Display for Auth {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Auth::NoToken => write!(f, "用户未登录"),
            Auth::TokenExpire => write!(f, "token过期"),
        }
    }
}

/// 错误转化
pub trait ToError<T, Error> {
    fn to_err<F>(self, f: F) -> Result<T, Error>
    where
        F: FnOnce() -> Error;
}

impl<T, E: Display, Error> ToError<T, Error> for Result<T, E> {
    fn to_err<F>(self, f: F) -> Result<T, Error>
    where
        F: FnOnce() -> Error,
    {
        match self {
            Ok(ok) => Ok(ok),
            Err(e) => {
                error!("发生错误: {}", e);
                Err(f())
            }
        }
    }
}

impl<T, Error> ToError<T, Error> for Option<T> {
    fn to_err<F>(self, f: F) -> Result<T, Error>
    where
        F: FnOnce() -> Error,
    {
        match self {
            Some(ok) => Ok(ok),
            None => {
                error!("Option is None");
                Err(f())
            }
        }
    }
}
